package com.gemantic.task.service.impl;


import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;
import com.gemantic.task.config.task.TaskPriority;
import com.gemantic.task.dao.constant.TaskStatus;
import com.gemantic.task.dao.repository.impl.TaskRepositoryImpl;
import com.gemantic.task.dto.DbTaskJobParams;
import com.gemantic.task.dto.ResResult;
import com.gemantic.task.dto.TaskQueryReqDto;
import com.gemantic.task.dto.TaskQueryRespDto;
import com.gemantic.task.handler.BaseTaskHandler;
import com.gemantic.task.handler.ITaskHandler;
import com.gemantic.task.runner.TaskRunner;
import com.gemantic.task.model.Task;
import com.gemantic.task.service.TaskService;
import com.gemantic.task.util.DateUtil;
import com.gemantic.task.util.EnvUtil;
import com.gemantic.task.util.TaskUtil;
import com.gemantic.task.util.ThreadPoolUtil;

/**
 * @author xiedi
 * @date 2022/09/16
 **/
@Service
public class TaskServiceImpl implements TaskService {
    @Autowired
    private TaskRunner taskRunner;

    @Autowired
    private TaskRepositoryImpl taskRepository;

    @Override
    public void closeTask(String orderNo, String taskType, String remark) {
        taskRepository.closeTask(orderNo, taskType, remark);
    }

    @Override
    public void runTask(String runnerName) {
        List<Task> list = taskRepository.getOpenTask();
        for (Task task : list) {
            task.setResponseData(null);
            runTask(task, runnerName);
        }
    }

    private ResResult runTask(Task task, String runnerName) {
        Map<String, String> args = new HashMap<>();
        args.put("runnerName", runnerName);
        return taskRunner.run(task, args);
    }

    @Override
    public ResResult runTask(long taskId, String runnerName) {
        Task task = taskRepository.getTaskById(taskId);
        if (task == null) {
            task = new Task();
            task.setId(taskId);
        }
        return this.runTask(task, runnerName);
    }

    @Override
    public List<Task> getOpenTaskByOrder(String orderNo, Class taskClass) {
        return getOpenTaskByOrder(orderNo, TaskUtil.getTaskType(taskClass));
    }

    @Override
    public Task getFirstOpenTaskByOrder(String orderNo, Class taskClass) {
        return getFirstOpenTaskByOrder(orderNo, TaskUtil.getTaskType(taskClass));
    }

    @Override
    public List<Task> getOpenTaskByOrder(String orderNo, String taskType) {
        return taskRepository.getOpenTaskByOrder(orderNo, taskType);
    }

    @Override
    public List<Task> getAllTaskByOrder(String orderNo, String taskType) {
        return taskRepository.getAllTaskByOrder(orderNo, taskType);
    }

    @Override
    public Task getFirstOpenTaskByOrder(String orderNo, String taskType) {
        List<Task> tasks = taskRepository.getOpenTaskByOrder(orderNo, taskType);
        if (CollectionUtils.isEmpty(tasks)) {
            return null;
        }
        return tasks.get(0);
    }


    @Override
    public <T extends BaseTaskHandler> Task createTask(String orderNo, Class<T> taskClass, Object req, TaskPriority priority) {
        return createTask(orderNo, taskClass, req, priority, null);
    }

    @Override
    public <T extends BaseTaskHandler> Task createTask(String orderNo, Class<T> taskClass, Object req, TaskPriority priority, int maxRetryTimes) {
        return createTaskWithStatus(orderNo, taskClass, req, priority, null, TaskStatus.OPEN, maxRetryTimes);
    }

    @Override
    public <T extends BaseTaskHandler> Task createTask(String orderNo,
                                                       Class<T> taskClass,
                                                       Object req,
                                                       TaskPriority priority,
                                                       Date nextRunDate) {
        int maxRetryTimes = TaskUtil.getMaxRetryTimes();
        return createTaskWithStatus(orderNo, taskClass, req, priority, nextRunDate, TaskStatus.OPEN, maxRetryTimes);
    }

    @Override
    public <T extends BaseTaskHandler> Task createTask(String orderNo, Class<T> taskClass, Object req, TaskPriority priority, Date nextRunDate, int maxRetryTimes) {
        return createTaskWithStatus(orderNo, taskClass, req, priority, nextRunDate, TaskStatus.OPEN, maxRetryTimes);
    }


    private <T extends ITaskHandler> Task createTaskWithStatus(String orderNo,
                                                               Class<T> taskClass,
                                                               Object req,
                                                               TaskPriority priority,
                                                               Date nextRunDate,
                                                               TaskStatus status,
                                                               int maxRetryTimes) {
        String taskType = TaskUtil.getTaskType(taskClass);
        return createTaskWithStatus(orderNo, taskType, req, priority, nextRunDate, status, maxRetryTimes);
    }

    @Override
    public Task createTask(String orderNo, String taskType, Object req, TaskPriority priority) {
        return createTask(orderNo, taskType, req, priority, null);
    }

    @Override
    public Task createTask(String orderNo, String taskType, Object req, TaskPriority priority, int maxRetryTimes) {
        return this.createTask(orderNo, taskType, req, priority, null, maxRetryTimes);
    }

    @Override
    public Task createTask(String orderNo, String taskType, Object req, TaskPriority priority, Date nextRunDate) {
        int maxRetryTimes = TaskUtil.getMaxRetryTimes();
        return createTaskWithStatus(orderNo, taskType, req, priority, nextRunDate, TaskStatus.OPEN, maxRetryTimes);
    }

    @Override
    public Task createTask(String orderNo, String taskType, Object req, TaskPriority priority, Date nextRunDate, int maxRetryTimes) {
        return this.createTaskWithStatus(orderNo, taskType, req, priority, nextRunDate, TaskStatus.OPEN, maxRetryTimes);
    }

    private Task createTaskWithStatus(String orderNo, String taskType, Object req, TaskPriority priority, Date nextRunDate, TaskStatus status,
                                      int maxRetryTimes) {
        if (nextRunDate == null
                || nextRunDate.before(DateUtil.now())) {
            nextRunDate = DateUtil.now();
        }
        if (maxRetryTimes <= 0) {
            maxRetryTimes = TaskUtil.getMaxRetryTimes();
        }
        Task task = new Task();
        task.setOrderNo(orderNo);
        task.setType(taskType);
        task.setRequestData(JSON.toJSONString(req));
        task.setPriority(priority.getValue());
        task.setNextRunTime(nextRunDate);
        task.setCreateTime(DateUtil.now());
        task.setStatus(status.getCode());
        task.setMaxRetryTimes(maxRetryTimes);
        task = taskRepository.createTask(task);
        return task;
    }


    @Override
    public void runFromDb(DbTaskJobParams params) {
        int totalServerCount = EnvUtil.getEnvConfig().getTotalServerCount();
        int serverSlice = EnvUtil.getEnvConfig().getServerSlice();
        List<Task> taskList = taskRepository.getActiveTasks(totalServerCount, serverSlice, params.getPriority(),
                params.getWithinHours(), params.getDelayMinute(), params.getLimit(), params.getWithPending());
        if (!CollectionUtils.isEmpty(taskList)) {
            String runnerName = String.format("Db_%s_%s", EnvUtil.getComputerName(), DateUtil.getTimestamp().toString());
            ThreadPoolUtil.execute((task, name, index) -> {
                this.runTask(task, name);
            }, taskList, runnerName, true);
        }
    }


    @Override
    public void batchUpdate(Date nextRunTime, String status, List<Long> tasks) {
        taskRepository.batchUpdate(nextRunTime, status, tasks);
    }

    @Override
    public TaskQueryRespDto getTasks(TaskQueryReqDto taskQueryReqDto) {
        return taskRepository.getTasks(taskQueryReqDto);
    }

    @Override
    public void modifyTaskFromUI(Task task) {
        taskRepository.modifyTaskFromUI(task);
    }

    @Override
    public void runFromMq(DbTaskJobParams params) {
        List<Task> taskList = taskRepository.getActiveTasks(1, 0, params.getPriority(),
                params.getWithinHours(), params.getDelayMinute(), params.getLimit(), params.getWithPending());
        if (!CollectionUtils.isEmpty(taskList)) {
            // toDoing
        }
    }
}
